package loveqq.niceexcel.read;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import loveqq.niceexcel.converter.CellData;
import loveqq.niceexcel.converter.ConvertContext;
import loveqq.niceexcel.converter.Converter;
import loveqq.niceexcel.converter.ConverterCache;
import loveqq.niceexcel.core.LocateDataHeadMetas;
import loveqq.niceexcel.core.MergedRegions;
import loveqq.niceexcel.core.metadata.CellHeadMeta;
import loveqq.niceexcel.enums.DataScopeEnum;
import loveqq.niceexcel.event.EventManager;
import loveqq.niceexcel.exception.LocatedDataConvertException;
import loveqq.niceexcel.exception.WorkbookCreateException;
import loveqq.niceexcel.read.handler.*;
import loveqq.niceexcel.util.CellUtils;
import loveqq.niceexcel.util.DateUtils;
import loveqq.niceexcel.util.NumberUtils;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ss.usermodel.*;
import org.springframework.cglib.beans.BeanMap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

import static loveqq.niceexcel.constant.DefaultConsts.DEFAULT_READ_HANDLER_CLASSES;

/**
 * @author zhongjh@tsintergy.com
 * @date 2022-08-17
 */
public class ExcelReader {
    /**
     * 读取配置
     */
    private final ReadConfig config;
    /**
     * 读取的workbook
     */
    private Workbook workbook;

    public ExcelReader(ReadConfig config) {
        this.config = config;
    }

    /**
     * 读取excel
     *
     * @return 解析数据
     */
    public <T> List<T> read(ReadSheet<T> readSheet) {
        try {
            return doRead(readSheet);
        } finally {
            DateUtils.removeThreadLocalCache();
            NumberUtils.removeThreadLocalCache();
        }
    }

    private <T> List<T> doRead(ReadSheet<T> readSheet) {
        List<T> result = new ArrayList<>();
        // 读取处理器(Workbook范围): 默认内置读取处理器 + Workbook范围读取处理器 + ReadSheet范围读取处理器
        List<ReadHandler> readHandlerList = new ArrayList<>();
        for (Class<? extends ReadHandler> readHandlerClass : DEFAULT_READ_HANDLER_CLASSES) {
            readHandlerList.add(ReflectUtil.newInstance(readHandlerClass));
        }
        readHandlerList.addAll(CollUtil.emptyIfNull(config.getReadHandlerList()));
        readHandlerList.addAll(CollUtil.emptyIfNull(readSheet.getReadHandlerList()));
        // 转换器缓存(ReadSheet范围): 默认内置转换器 + Workbook范围转换器 + ReadSheet范围转换器
        ConverterCache converterCache = new ConverterCache();
        CollUtil.emptyIfNull(config.getConverterList()).forEach(converterCache::put);
        CollUtil.emptyIfNull(readSheet.getConverterList()).forEach(converterCache::put);
        // 读取上下文
        ReadContext context = new ReadContext();
        context.setHeadClass(readSheet.getHeadClass());
        context.setEventManager(new EventManager());

        // workbook读取开始
        if (this.workbook == null) {
            try {
                this.workbook = WorkbookFactory.create(config.getInputStream());
            } catch (IOException | EncryptedDocumentException e) {
                throw new WorkbookCreateException(e);
            }
        }
        invokeListener(WorkbookReadHandler.class, readHandlerList, handler -> handler.onWorkbookBegin(context, workbook));

        for (Sheet sheet : getSheetList(readSheet, workbook)) {
            // sheet读取开始
            context.setLocateDataHeadMetas(new LocateDataHeadMetas());
            invokeListener(SheetReadHandler.class, readHandlerList, handler -> handler.onSheetBegin(context, sheet));
            context.setMergedRegions(new MergedRegions(sheet));

            ConvertContext convertContext = new ConvertContext();
//            BeanMap commonBean = createBean(readSheet.getHeadClass(), null);
//            BeanMap currentBean = null;
            T commonBean = createBean(readSheet.getHeadClass(), null);
            T currentBean = null;

            for (Row row : sheet) {
                if (row == null) {
                    continue;
                }
                // row读取开始
                invokeListener(RowReadHandler.class, readHandlerList, handler -> handler.onRowBegin(context, row));

                for (Cell cell : row) {
                    if (cell == null) {
                        continue;
                    }
                    // 设置合并区域的第一个单元格, 方便读取数据在合并区域能正确获取值
                    context.getMergedRegions().setRegionFirstCell(cell);
                    // cell读取
                    invokeListener(CellReadHandler.class, readHandlerList, handler -> handler.onCellRead(context, cell));
                    // cell全部定位数据开始前, 拦截决定是否继续定位
                    if (!invokeInterceptor(LocateDataReadHandler.class, readHandlerList, handler -> handler.beforeLocateData(context, cell))) {
                        continue;
                    }
                    // 定位单元格数据处理
                    for (CellHeadMeta cellHeadMeta : context.getLocateDataHeadMetas()) {
                        // 如果定位表头rowIndex < 0, 表示竖向解析即每一行都有对应的列表头
                        int rowIndex = (cellHeadMeta.getRowIndex() >= 0) ? cellHeadMeta.getRowIndex() : cell.getRowIndex();
                        int columnIndex = cellHeadMeta.getColumnIndex();
                        if (cell.getColumnIndex() == columnIndex && cell.getRowIndex() == rowIndex) {
                            // 已成功定位单元格, 开始处理数据前拦截, 决定是否继续处理单元格数据
                            if (!invokeInterceptor(LocateDataReadHandler.class, readHandlerList, handler -> handler.onLocatedDataBegin(context, cell, cellHeadMeta))) {
                                continue;
                            }
//                            BeanMap bean;
                            T bean;
                            if (DataScopeEnum.COMMON == context.getDataScope()) {
                                // 相同的属性数据, 暂不加入result
                                bean = commonBean;
                                context.resetDataScope();
                            } else {
                                if (currentBean == null) {
                                    currentBean = createBean(readSheet.getHeadClass(), commonBean);
//                                    result.add((T) currentBean.getBean());
                                    result.add(currentBean);
                                }
                                bean = currentBean;
                            }
                            // 设置转换上下文
                            convertContext.setDateTimeFormat(cellHeadMeta.getDateTimeFormat());
                            convertContext.setNumberFormat(cellHeadMeta.getNumberFormat());
                            // 转换数据设置到属性(合并区域自动取有数据的单元格)
                            Cell targetCell = context.getMergedRegions().getRegionFirstCell(cell);
                            CellData<?> cellData = CellUtils.getCellData(targetCell);
                            Converter<?> converter = cellHeadMeta.getConverter();
                            if (converter == null) {
                                Class<?> javaType = cellHeadMeta.getField().getType();
                                converter = converterCache.get(javaType, cell.getCellType());
                                if (converter == null) {
                                    String message = String.format("找不到Converter: javaType=[%s], cellType=[%s]", javaType.getName(), cell.getCellType());
                                    throw new LocatedDataConvertException(message);
                                }
                            }
                            try {
                                Object javaData = converter.convertToJavaData(cellData, convertContext);
//                                bean.put(cellHeadMeta.getField().getName(), javaData);
                                cellHeadMeta.getField().set(bean, javaData);
                            } catch (Exception e) {
                                throw new LocatedDataConvertException(e, readSheet);
                            }
                            // 定位单元格处理数据结束
                            invokeListener(LocateDataReadHandler.class, readHandlerList, handler -> handler.onLocatedDataEnd(context, cell, cellHeadMeta));
                        }
                    }
                    // cell全部定位数据处理结束
                    invokeListener(LocateDataReadHandler.class, readHandlerList, handler -> handler.afterLocateData(context, cell));
                }
                currentBean = null; // 如果独立数据下一行是新对象

                // row读取结束
                invokeListener(RowReadHandler.class, readHandlerList, handler -> handler.onRowEnd(context, row));
            }

            // sheet读取结束
            invokeListener(SheetReadHandler.class, readHandlerList, handler -> handler.onSheetEnd(context, sheet));
        }

        // work读取结束
        invokeListener(WorkbookReadHandler.class, readHandlerList, handler -> handler.onWorkbookEnd(context, workbook));

        return result;
    }

    /**
     * 根据配置获取sheet集合
     */
    private <T> List<Sheet> getSheetList(ReadSheet<T> readSheet, Workbook workbook) {
        List<Sheet> result = new ArrayList<>();
        if (readSheet.isAllSheet()) {
            for (Sheet sheet : workbook) {
                result.add(sheet);
            }
        } else if (CollUtil.isNotEmpty(readSheet.getSheetIndexes())) {
            for (int sheetIndex : readSheet.getSheetIndexes()) {
                result.add(workbook.getSheetAt(sheetIndex));
            }
        } else if (CollUtil.isNotEmpty(readSheet.getSheetNames())) {
            for (String sheetName : readSheet.getSheetNames()) {
                result.add(workbook.getSheet(sheetName));
            }
        }
        return result;
    }

    /**
     * 创建新的返回数据对象
     *
     * @param copyBean 拷贝的数据对象(一般是共同数据对象)
     * @return 新数据对象
     */
//    private <T> BeanMap createBean(Class<T> clazz, BeanMap copyBean) {
//        T bean = ReflectUtil.newInstance(clazz);
//        BeanMap result = BeanMap.create(bean);
//        if (CollUtil.isNotEmpty(copyBean)) {
//            result.putAll(copyBean);
//        }
//        return result;
//    }
    private <T> T createBean(Class<T> clazz, T copyBean) {
        T result = ReflectUtil.newInstance(clazz);
        if (copyBean == null) {
            BeanUtil.copyProperties(copyBean, result);
        }
        return result;
    }

    /**
     * 执行处理器监听方法
     *
     * @param handlerType     读取监听器子类
     * @param readHandlerList 读取处理器
     * @param listen          监听动作
     */
    private <T extends ReadHandler> void invokeListener(Class<T> handlerType, List<ReadHandler> readHandlerList, Consumer<T> listen) {
        for (ReadHandler listener : readHandlerList) {
            if (handlerType.isAssignableFrom(listener.getClass())) {
                listen.accept((T) listener);
            }
        }
    }

    /**
     * 执行处理器拦截方法
     *
     * @param interceptorType 读取拦截器子类
     * @param readHandlerList 读取处理器
     * @param intercept       拦截动作
     * @return 拦截是否继续往下走
     */
    private <T extends ReadHandler> boolean invokeInterceptor(Class<T> interceptorType, List<ReadHandler> readHandlerList, Predicate<T> intercept) {
        for (ReadHandler interceptor : readHandlerList) {
            if (interceptorType.isAssignableFrom(interceptor.getClass()) && !intercept.test((T) interceptor)) {
                return false;
            }
        }
        return true;
    }

}
